<?php
// +----------------------------------------------------------------------
// | View 视图服务
// +----------------------------------------------------------------------
// | * 视图层
// | * 1.查询所有关联表的字段
// | * 2.处理字段（可显示、可编辑、可排序、可分组、可过滤）、还要通过临时字段、字段权限进行判断，组装出view需要的字段
// | * 3.组装view视图的表格列
// +----------------------------------------------------------------------
// | 错误编码头 231xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\model\EntityModel;
use common\model\ProjectRelationTemplateTodoModel;
use common\model\StatusModel;
use common\model\TaskMemberModel;
use common\model\TaskModel;
use common\model\TaskRelationModel;
use common\model\TemplateTodoModel;
use common\model\TemplateTodoFileModel;
use common\model\UserModel;
use Exception;
use support\ErrorCode;
use support\SingletonTrait;
use Throwable;


class TemplateTodoService
{
    use SingletonTrait;

    /**
     * 添加/更新
     * @param $data
     * @return bool|float|int|string
     * @throws Exception|Throwable
     */
    public function save($data)
    {
        foreach (['start_man', 'participant_man', 'job_option', 'after_option', 'upstream_downstream_template'] as $jsonField) {
            $data[$jsonField] = json_encode($data[$jsonField] ?? []);
        }
        $fileList = $data['file_list'] ?? [];
        unset($data['file_list']);

        model(TemplateTodoModel::class)->startTrans();
        try {
            if (!isset($data['id'])) {
                $data['tenant_id'] = auto_fill_tenant_id();
                $data['created'] = time();
                $data['created_by'] = fill_created_by();
                $res = model(TemplateTodoModel::class)->add($data);
                $templateTodoId = $res;
            } else {
                $data['updated'] = time();
                $data['updated_by'] = fill_created_by();
                $res = model(TemplateTodoModel::class)->save($data);
                $templateTodoId = $data['id'];
            }
            if (!empty($templateTodoId)) {
                $this->saveTemplateTodoFileList($templateTodoId, $fileList);
            }
            model(TemplateTodoModel::class)->commit();
            return $res;
        } catch (Throwable $e) {
            model(TemplateTodoModel::class)->rollback();
            throw $e;
        }
    }

    /**
     * filter删除
     * @param $filter
     * @return false|float|int|mixed|string
     * @throws Throwable
     */
    public function delete($filter)
    {
        $templateTodoIdList = model(TemplateTodoModel::class)->where($filter)->field('id')->select();
        if (empty($templateTodoIdList)) {
            return 0;
        }
        $templateTodoIdList = array_column($templateTodoIdList, 'id');

        model(TemplateTodoModel::class)->startTrans();
        try {
            model(TemplateTodoFileModel::class)
                ->where(['template_todo_id' => ['IN', $templateTodoIdList]])
                ->delete();
            $res = model(TemplateTodoModel::class)->where(['id' => ['IN', $templateTodoIdList]])->delete();
            model(TemplateTodoModel::class)->commit();
            return $res;
        } catch (Throwable $e) {
            model(TemplateTodoModel::class)->rollback();
            throw $e;
        }
    }

    /**
     * 任务模板 待办清单拓扑排序
     * @param $projectTemplateItemList
     * @return array|mixed
     */
    private function reOrderTemplateTodoUpstreamDownstreamTemplate($projectTemplateItemList = [])
    {
        // 建立合法图结构
        $nodeMap = [];
        $nodeAllowList = array_column($projectTemplateItemList, 'id');
        $projectTemplateItemList = array_column($projectTemplateItemList, null, 'id');
        foreach ($projectTemplateItemList as $item) {
            if (!isset($nodeMap[$item['id']])) {
                $nodeMap[$item['id']] = ['upstream_list' => []];
            }
            $nodeMap[$item['id']]['upstream_list'] = array_merge($nodeMap[$item['id']]['upstream_list'], array_intersect(array_column($item['upstream_task'] ?? [], 'target_id'), $nodeAllowList));
        }
        // 排序 && 环路检测
        $hasCircle = false;
        $deleteCount = 0;
        $orderList = [];
        while (!empty($nodeMap) && $hasCircle === false) {
            $nodeId = -1;
            foreach ($nodeMap as $id => $item) {
                if (empty($item['upstream_list']) && !isset($item['is_delete'])) {
                    $nodeId = $id;
                    break;
                }
            }
            // 无可删节点时跳出
            if ($nodeId == -1) {
                $hasCircle = true;
                break;
            }
            $orderList[] = $nodeId;
            $nodeMap[$nodeId]['is_delete'] = true;
            $deleteCount++;
            foreach ($nodeMap as &$item) {
                $item['upstream_list'] = array_diff($item['upstream_list'], [$nodeId]);
            }
        }
        $res = [];
        if ($deleteCount == count($projectTemplateItemList) || !$hasCircle) {
            // 重新组织结果
            foreach ($orderList as $id) {
                if (empty($projectTemplateItemList[$id] ?? [])) {
                    // 出错则原样返回
                    $res = $projectTemplateItemList;
                    break;
                }
                $res[] = $projectTemplateItemList[$id];
            }
        } else {
            // 存在环路则原样返回
            $res = $projectTemplateItemList;
        }
        return $res;
    }


    /**
     * @param $param
     * @return array
     * @throws Exception
     */
    public function list($param)
    {
        $projectParamFilter = [];
        $projectId = $param['project_id'] ?? 0;
        if (!empty($projectId)) {
            $projectParamFilter[] = ['project_id' => $projectId];
        }

        // 过滤项目配置里没有启用的模板
        if (isset($param['is_open'])) {
            $projectParamFilter[] = ['is_open' => $param['is_open']];
        }

        $projectTemplateTodoData = model(ProjectRelationTemplateTodoModel::class)->selectData([
            'filter' => $projectParamFilter,
            'fields' => 'template_todo_id,project_id,step_category_config,start_man,participant_man,job_option,after_option,is_open,upstream_downstream_template'
        ]);

        foreach ($projectTemplateTodoData['rows'] as &$item) {
            if (empty($item['upstream_downstream_template'])) {
                continue;
            }
            $item['upstream_downstream_template'] = $this->reOrderTemplateTodoUpstreamDownstreamTemplate($item['upstream_downstream_template']);
        }

        if (isset($param['is_open'])) {
            if (empty($projectTemplateTodoData['rows'])) {
                return ['rows' => [], 'total' => 0];
            }
            $param['filter'] = ['id' => ['IN', join(',', array_column($projectTemplateTodoData['rows'], 'template_todo_id'))]];
        }

        $projectTemplateTodoMap = [];
        if (!empty($projectTemplateTodoData['rows']) && !empty($projectId)) { // 只有客户端调用,传项目id的时候才匹配模板执行人
            $projectTemplateTodoMap = array_column($projectTemplateTodoData['rows'], null, 'template_todo_id');
        }

        // 查询待办模板列表
        $tempLateTodoList = model(TemplateTodoModel::class)->selectData($param);
        if (empty($tempLateTodoList['total'])) {
            return ['rows' => [], 'total' => 0];
        }
        // 组织环节
        $stepCategoryMap = [];
        $producerStepCategoryMap = [];
        $producerStepCategoryList = ProjectService::getInstance()->getShowStepCategoryList('');
        foreach ($producerStepCategoryList as $item) {
            $producerStepCategoryMap[$item['show_name']][] = ['show_name' => $item['show_name'], 'id' => $item['id'], 'code' => $item['code']];
            $stepCategoryMap[$item['id']] = ['id' => $item['id'], 'name' => $item['name']];
        }
        // 组织角色数据
        $tempLateTodoConfig = OptionsService::getInstance()->getOptionsData('template_todo_config');

        $stageList = StageService::getInstance()->getStageListLite([]);
        $templateTodoFiles = $this->getTemplateTodoFileList(['template_todo_id' => ['IN', array_column($tempLateTodoList['rows'], 'id')]]);
        $templateTodoFiles = array_combine(array_column($templateTodoFiles, 'template_todo_id'), array_column($templateTodoFiles, 'file_list'));

        $rows = $this->templateTodoList($tempLateTodoList['rows'], $producerStepCategoryMap, $stepCategoryMap, $projectTemplateTodoMap, $tempLateTodoConfig, $stageList, $templateTodoFiles);
        return ['rows' => $rows, 'total' => $tempLateTodoList['total']];
    }

    /**
     * 查询单条
     * @param $id
     * @return array|mixed
     * @throws Exception
     */
    public function getOne($id)
    {
        $list = $this->list(['filter' => ['id' => $id], 'fields' => 'id,name,type,stage_id,step_category_id,start_man,participant_man,job_option,after_option,upstream_downstream_template']);
        if (empty($list['rows']['0'])) {
            return [];
        }
        return $list['rows']['0'];
    }

    /**
     * 处理返回参数
     * @param $list
     * @param $producerStepCategoryMap
     * @param $stepCategoryMap
     * @param $projectTemplateTodoMap
     * @param $tempLateTodoConfig
     * @return array
     */
    public function templateTodoList($list, $producerStepCategoryMap, $stepCategoryMap, $projectTemplateTodoMap, $tempLateTodoConfig, $stageList, $templateTodoFiles)
    {
        if (empty($list) || empty($producerStepCategoryMap) || empty($stepCategoryMap) || empty($tempLateTodoConfig)) {
            return [];
        }
        return array_map(function ($tmp) use ($tempLateTodoConfig, $producerStepCategoryMap, $stepCategoryMap, $projectTemplateTodoMap, $stageList, $templateTodoFiles) {
            $participantManRoleData = [];
            if (!empty($tmp['participant_man']['role'])) {
                foreach ($tmp['participant_man']['role'] as $id) {
                    if (empty($tempLateTodoConfig[$id])) {
                        continue;
                    }
                    $participantManRoleData[] = ['id' => $id, 'name' => $tempLateTodoConfig[$id], 'children_ids' => [$id], 'type' => 'role'];
                }
            }
            if (!empty($tmp['participant_man']['step_category'])) {
                $participantManShowStepCategory = $this->stepCategoryShowNameGroupList($tmp['participant_man']['step_category'], $producerStepCategoryMap);
                foreach ($participantManShowStepCategory as $id => $value) {
                    $participantManRoleData[] = ['id' => $id, 'name' => $id, 'children_ids' => $value, 'type' => 'step_category'];
                }
            }
            $startMan = [];
            if (!empty($tmp['start_man']['role']) && !empty($tempLateTodoConfig[$tmp['start_man']['role']])) {
                $startMan[] = ['id' => $tmp['start_man']['role'], 'name' => $tempLateTodoConfig[$tmp['start_man']['role']], 'children_ids' => [$tmp['start_man']['role']], 'type' => 'role'];
            }
            if (!empty($tmp['start_man']['step_category'])) {
                $startManShowStepCategory = $this->stepCategoryShowNameGroupList($tmp['start_man']['step_category'], $producerStepCategoryMap);
                foreach ($startManShowStepCategory as $id => $value) {
                    $startMan[] = ['id' => $id, 'name' => $id, 'children_ids' => $value, 'type' => 'step_category'];
                }
            }
            // 关联环节
            if (!empty($tmp['step_category_id'])) {
                $stepCategoryIds = explode(',', $tmp['step_category_id']);
                $startManShowStepCategory = $this->stepCategoryShowNameGroupList($stepCategoryIds, $producerStepCategoryMap);
                $tmpStepCategoryId = [];
                foreach ($startManShowStepCategory as $id => $value) {
                    $tmpStepCategoryId = ['id' => $id, 'name' => $id, 'children_ids' => $value, 'type' => 'step_category'];
                }
                $tmp['step_category_id'] = $tmpStepCategoryId;
            }
            // 关联阶段
            if (!empty($tmp['stage_id'])) {
                $stage = $stageList[$tmp['stage_id']] ?? '';
                // 关联阶段所属环节
                if (!empty($stage['step_category_id'])) {
                    $stepCategoryIds = explode(',', $stage['step_category_id']);
                    $startManShowStepCategory = $this->stepCategoryShowNameGroupList($stepCategoryIds, $producerStepCategoryMap);
                    $tmpStepCategoryId = [];
                    foreach ($startManShowStepCategory as $id => $value) {
                        $tmpStepCategoryId = ['id' => $id, 'name' => $id, 'children_ids' => $value, 'type' => 'step_category'];
                    }
                    $stage['step_category_id'] = $tmpStepCategoryId;
                }
                $tmp['stage'] = $stage;
            }
            // 关联交付文件
            $tmp['file_list'] = $templateTodoFiles[$tmp['id']] ?? [];
            // 填充项目模板待办数据
            $fixStartManData = $projectTemplateTodoMap[$tmp['id']] ?? null;
            if (!empty($fixStartManData)) {
                // 填充发起人
                if (!empty($fixStartManData['start_man'])) {
                    $startMan = [];
                    foreach ($fixStartManData['start_man'] as $templateTodoStartManItem) {
                        $startMan[] = $templateTodoStartManItem;
                    }
                }
                // 填充执行人
                if (!empty($fixStartManData['participant_man'])) {
                    $participantManRoleData = [];
                    foreach ($fixStartManData['participant_man'] as $templateTodoParticipantManItem) {
                        $participantManRoleData[] = $templateTodoParticipantManItem;
                    }
                }
                // 填充关联环节
                if (!empty($fixStartManData['step_category_config'])) {
                    $templateTodoStepCategoryData = [];
                    foreach ($fixStartManData['step_category_config'] as $templateTodoStepCategoryItem) {
                        $templateTodoStepCategoryData[] = $templateTodoStepCategoryItem;
                    }
                    $tmp['step_category_id'] = $templateTodoStepCategoryData;
                }
                // 填充准备事项
                if (!empty($fixStartManData['job_option'])) {
                    $templateTodoJobOptionData = [];
                    foreach ($fixStartManData['job_option'] as $TemplateTodoJobOptionItem) {
                        $templateTodoJobOptionData[] = $TemplateTodoJobOptionItem;
                    }
                    $tmp['job_option'] = $templateTodoJobOptionData;
                }
                // 填充后续事项
                if (!empty($fixStartManData['after_option'])) {
                    $templateTodoAfterOptionData = [];
                    foreach ($fixStartManData['after_option'] as $templateTodoAfterOption) {
                        $templateTodoAfterOptionData[] = $templateTodoAfterOption;
                    }
                    $tmp['after_option'] = $templateTodoAfterOptionData;
                }
                // 填充上下游任务清单
                if (!empty($fixStartManData['upstream_downstream_template'])) {
                    $upstreamDownstreamTemplateData = [];
                    foreach ($fixStartManData['upstream_downstream_template'] as $upstreamDownstreamTemplate) {
                        $upstreamDownstreamTemplateData[] = $upstreamDownstreamTemplate;
                    }
                    $tmp['upstream_downstream_template'] = $upstreamDownstreamTemplateData;
                }
                // 是否启用
                $tmp['is_open'] = $fixStartManData['is_open'];
            }
            $tmp['start_man'] = $startMan;
            $tmp['participant_man'] = $participantManRoleData;
            unset($participantManRoleData);
            unset($jobOptionRoleData);
            return $tmp;
        }, $list);
    }

    /**
     * 处理大环节分类
     * @param $showCategoryIds
     * @param $producerStepCategoryMap
     * @return array
     */
    public function stepCategoryShowNameGroupList($showCategoryIds, $producerStepCategoryMap)
    {
        if (empty($showCategoryIds)) {
            return [];
        }
        $stepCategoryShowNameGroup = [];
        foreach ($showCategoryIds as $participantManItem) {
            foreach ($producerStepCategoryMap as $showName => $producerStepCategoryItem) {
                $ids = array_column($producerStepCategoryItem, 'id');
                if (in_array($participantManItem, $ids)) {
                    $stepCategoryShowNameGroup[$showName] = $ids;
                }
            }
        }
        return $stepCategoryShowNameGroup;
    }

    /**
     * 创建模板待办
     * @param $data
     * @return void
     * @throws Throwable
     * @throws \think\Exception
     */
    public function createTemplateTodo($data)
    {
        if (empty($data['template_todo_list']) || empty($data['task'])) {
            return;
        }
        $templateTodoList = $data['template_todo_list'];
        $task = $data['task'];


        $linkModuleCodeEpisodeName = '';
        if (!empty($data['relate_tag'])) {
            $linkModuleCodeEpisode = array_column($data['relate_tag'], null, 'link_module_code');
            if (!empty($linkModuleCodeEpisode['episode']['link_id'])) {
                $episodeData = model(EntityModel::class)
                    ->where(['id' => $linkModuleCodeEpisode['episode']['link_id']])
                    ->field('id,name')
                    ->find();
                $linkModuleCodeEpisodeName = $episodeData['name'] . '/';
            };
        }

        foreach ($templateTodoList as $templateTodoItem) {
            $todoData = [
                'task' => [
                    'description' => $task['description'] ?? '',
                    'name' => $linkModuleCodeEpisodeName . $templateTodoItem['option_name'] ?? '',
                    'plan_end_time' => $templateTodoItem['plan_end_time'] ?? '',
                    'template_type' => $templateTodoItem['template_type'],
                    'template_sort' => $templateTodoItem['template_sort'],
                    'task_is_append' => $task['task_is_append'] ?? 'no',
                    'project_id' => $task['project_id'] ?? 0,
                    'step_type' => TaskModel::STEP_TYPE_STANDARD_MAKE_TO_DO,
                    'template_todo_parent_task_id' => $task['id'] // 模板待办父id
                ],
                'executor_list' => $templateTodoItem['executor_list'] ?? null,
                'starter_list' => $templateTodoItem['starter_list'] ?? null,
                'template_todo_id' => $data['template_todo_id'] ?? null,
                'relate_tag' => $data['relate_tag'] ?? null,
            ];
            $todoRes = TaskMemberService::getInstance()->create($todoData, request()->getUserId(), request()->tenantId);
            $taskRelationData = [
                'source_task_id' => $task['id'],
                'target_task_id' => $todoRes['task']['id'],
                'relation_type' => TaskRelationModel::RELATION_TYPE_TEMPLATE_TODO
            ];
            $projectId = $task['project_id'] ?? 0;
            // 绑定关联关系
            $requestRelation = model(TaskRelationModel::class)
                ->setSuffix(sprintf('_project_%d', $projectId))
                ->addItem($taskRelationData);
            if (!$requestRelation) {
                throw new LogicException('bind template todo task fail', ErrorCode::BIND_DISMANTLE_RELATION_TASK_FAIL);
            }
        }
    }



    /**
     * 保存关联交付列表
     * @param $templateTodoId
     * @param array $templateFileList
     * @return true
     * @throws Throwable
     */
    public function saveTemplateTodoFileList($templateTodoId, $templateFileList = [])
    {
        $currentTime = time();

        // 已保存列表
        $savedFileList = model(TemplateTodoFileModel::class)
            ->where(['template_todo_id' => $templateTodoId])
            ->field('id, category, content')
            ->select();
        $savedFileCategory = array_combine(array_column($savedFileList, 'category'), array_column($savedFileList, 'id'));

        // 上传列表
        $targetFileList = [];
        foreach ($templateFileList as $item) {
            $targetFileList[$item['category']] = isset($targetFileList[$item['category']])
                ? array_merge($targetFileList[$item['category']], $item['file'])
                : $item['file'];
        }
        array_walk($targetFileList, function (&$item) {
            $item = array_unique($item);
            $item = implode(',', $item);
        });

        $stageFileAdd = [];
        $stageFileUpdate = [];
        foreach ($targetFileList as $category => $list) {
            if (!isset($savedFileCategory[$category])) {
                // 待添加
                $stageFileAdd[] = [
                    'template_todo_id' => $templateTodoId,
                    'category' => $category,
                    'content' => $list,
                    'created_by' => fill_created_by(),
                    'created' => $currentTime
                ];
            } else {
                // 待更新
                $stageFileUpdate[] = [
                    'id' => $savedFileCategory[$category],
                    'content' => $list,
                    'updated_by' => fill_created_by(),
                    'updated' => $currentTime
                ];
                unset($savedFileCategory[$category]);
            }
        }

        // 待移除
        $stageFileRemove = array_values($savedFileCategory);

        model(TemplateTodoFileModel::class)->startTrans();
        try {
            if (!empty($stageFileRemove)) {
                model(TemplateTodoFileModel::class)
                    ->where(['id' => ['IN', $stageFileRemove]])
                    ->delete();
            }
            if (!empty($stageFileUpdate)) {
                model(TemplateTodoFileModel::class)
                    ->saveAll($stageFileUpdate);
            }
            if (!empty($stageFileAdd)) {
                model(TemplateTodoFileModel::class)
                    ->addAll($stageFileAdd);
            }
            model(TemplateTodoFileModel::class)->commit();
            return true;
        } catch (Throwable $e) {
            model(TemplateTodoFileModel::class)->rollback();
            throw $e;
        }
    }

    /**
     * 获取关联交付列表
     * @param $filter
     * @return array
     */
    public function getTemplateTodoFileList($filter)
    {
        $fileList = model(TemplateTodoFileModel::class)
            ->where($filter)
            ->field('id, template_todo_id, category, content')
            ->select();
        array_walk($fileList, function (&$item) {
            $item['id'] = intval($item['id']);
            $item['file'] = explode(',', $item['content']);
            unset($item['content']);
        });
        $templateFileList = [];
        foreach ($fileList as $item) {
            $templateFileList[intval($item['template_todo_id'])][] = [
                'id' => $item['id'],
                'category' => $item['category'],
                'file' => $item['file'],
            ];
        }
        $res = [];
        foreach ($templateFileList as $key => $item) {
            $res[] = [
                'template_todo_id' => $key,
                'file_list' => $item
            ];
        }

        return $res;
    }


    public function getOpenTemplateTodoStageList($projectId, $page, $isOpen = true)
    {
        return model(ProjectRelationTemplateTodoModel::class)
            ->join("template_todo on template_todo.id = project_relation_template_todo.template_todo_id")
            ->join("stage on stage.id = template_todo.stage_id")
            ->where([
                'project_relation_template_todo.project_id' => $projectId,
                'project_relation_template_todo.is_open' => $isOpen ? "yes" : "no"
            ])
            ->field("stage.id,stage.name")
            ->group("stage.id")
            ->page(...$page)
            ->select();
    }


}

